package com.wao.file.web.rest;

import com.codahale.metrics.annotation.Timed;
import com.wao.file.domain.BatchStatus;
import com.wao.file.domain.FileInfo;
import com.wao.file.domain.FileKey;
import com.wao.file.service.QiniuFileStorage;
import com.wao.file.web.rest.vm.BatchKeyVM;
import com.wao.file.web.rest.vm.FileMoveVM;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.validation.Valid;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * REST controller for managing File.
 */
@RestController
@RequestMapping("/api")
public class FileResource {

    private final Logger log = LoggerFactory.getLogger(FileResource.class);

    private final QiniuFileStorage qiniuFileStorage;

    public FileResource(QiniuFileStorage qiniuFileStorage) {
        this.qiniuFileStorage = qiniuFileStorage;
    }


    /**
     * GET  /token : get file upload token .
     *
     * @param key the name of the file to retrieve
     * @return the ResponseEntity with status 200 (OK) and with body the FileInfo,
     * or with status 500 (Internal server error) if failed to get the file info.
     */
    @GetMapping("/token")
    @Timed
    public ResponseEntity<Map<String, String>> getUploadToken(@RequestParam(name = "key", required = false) String key) {
        log.debug("REST request to get File Info : {}", key);
        return ResponseEntity.ok(Collections.singletonMap("uptoken", qiniuFileStorage.uploadToken(key)));
    }

    /**
     * GET  /domains : get a list of available domains .
     *
     * @return the ResponseEntity with status 200 (OK) and with body the FileInfo,
     * or with status 500 (Internal server error) if failed to get the file info.
     */
    @GetMapping("/domains")
    @Timed
    public ResponseEntity<List<String>> getDomains() {
        log.debug("REST request to get a list of available domains");
        List<String> domains = new ArrayList<>();
        try {
            domains = qiniuFileStorage.getDomains();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return ResponseEntity.ok(domains);
    }

    /**
     * POST  /files : upload a file .
     *
     * @param file the file to upload.
     * @param path the new name of the file on the file server
     * @return the ResponseEntity with status 200 (upload success) and with body the new FileKey,
     * or with status 400 (Bad Request) if the uploading file is empty
     * or with status 500 (Internal Server Error) if upload failed.
     * @throws URISyntaxException if the Location URI syntax is incorrect
     */
    @PostMapping("/files")
    @Timed
    public ResponseEntity<?> upload(@RequestParam(name = "path", required = false) String path,
                                    @RequestParam(name = "file", required = true) MultipartFile file,
                                    MultipartHttpServletRequest request) {
        log.debug("REST request to upload a file {} to {}", file, path);
        if (file == null || file.isEmpty())
            return new ResponseEntity<>("the file uploaded can not be empty", HttpStatus.BAD_REQUEST);
        if (StringUtils.isBlank(path))
            path = file.getOriginalFilename();
        try {
            FileKey fileKey = qiniuFileStorage.upload(file.getInputStream(), path);
            return ResponseEntity.ok(fileKey);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("file upload failed: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * GET  /files/{key} : get file by key (file name) .
     *
     * @param key the name of the file to retrieve
     * @return the ResponseEntity with status 200 (OK) and with body the FileInfo,
     * or with status 500 (Internal server error) if failed to get the file info.
     */
    @GetMapping(value = "/files/stat")
    @Timed
    public ResponseEntity<?> stat(@RequestParam(name = "key", required = true) String key) {
        log.debug("REST request to get File Info : {}", key);
        try {
            FileInfo fileInfo = qiniuFileStorage.stat(key);
            return ResponseEntity.ok().body(fileInfo);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("failed to get file info: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * GET  /files : get all files .
     *
     * @param prefix the prefix of the files
     * @param marker
     * @param limit
     * @return the ResponseEntity with status 200 (OK) and with body the FileListing,
     * or with status 500 (Internal server error) if failed to get the file list.
     */
    @GetMapping("/files")
    @Timed
    public ResponseEntity<?> getFileList(@RequestParam(name = "prefix", required = false) String prefix,
                                         @RequestParam(name = "marker", required = false) String marker,
                                         @RequestParam(name = "limit", defaultValue = "100", required = false) Integer limit,
                                         @RequestParam(name = "delimiter", defaultValue = "", required = false) String delimiter) {
        log.debug("REST request to get a list of files");
        try {
            return ResponseEntity.ok(qiniuFileStorage.listFiles(prefix, marker, limit, delimiter));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("failed to get file list: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * POST  /files : upload a file .
     *
     * @param fileMoveVM the file keys.
     * @return the ResponseEntity with status 200 (upload success) ,
     * or with status 500 (Internal Server Error) if move failed.
     * @throws URISyntaxException if the Location URI syntax is incorrect
     */
    @PostMapping(value = "/files/move")
    @Timed
    public ResponseEntity<?> move(@Valid @RequestBody FileMoveVM fileMoveVM) {
        log.debug("REST request to move file: {}", fileMoveVM);
        try {
            qiniuFileStorage.move(fileMoveVM.getKey(), fileMoveVM.getPath(), true);
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("file move failed: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * DELETE  /files/{key} : delete the file .
     *
     * @param key the name of the file to delete
     * @return the ResponseEntity with status 200 (OK)
     */
    @DeleteMapping(value = "/files")
    @Timed
    public ResponseEntity<?> delete(@RequestParam(name = "key", required = true) String key) {
        log.debug("REST request to delete file : {}", key);
        try {
            qiniuFileStorage.delete(key);
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("failed to delete file : " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * DELETE  /files/batch : delete the file .
     *
     * @param keys the name of the files to delete
     * @return the ResponseEntity with status 200 (OK)
     */
    @DeleteMapping("/files/batch")
    @Timed
    public ResponseEntity<?> batchDelete(@Valid @RequestBody BatchKeyVM keys) {
        log.debug("REST request to batch delete files : {}", keys);
        try {
            List<BatchStatus> statuses = qiniuFileStorage.batchDelete(keys.getKeysArray());
            return ResponseEntity.ok(statuses);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return new ResponseEntity<>("file batch delete failed : " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
